﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using Facebook;
using IniParser;

using FacebookClient.Database;

namespace FacebookClient
{
    public class Client
    {
        #region Properties
        private readonly string AppId = (string)Properties.Settings.Default["AppId"]; //"162822957252165";
        private readonly string AppSecret = (string)Properties.Settings.Default["AppSecret"];
        private readonly string SettingFile = (string)Properties.Settings.Default["SettingFile"]; //"Facebook.ini"; 
        private readonly string Section = "Facebook";
        private readonly string AccessToken = "AccessToken";

        private string _extendedPermissions = "user_about_me,read_stream,user_friends";
        public string ExtendedPermissions
        {
            get { return _extendedPermissions; }
            set { _extendedPermissions = value; }
        }

        private User _me;
        public User Me
        {
            get { return _me; }
        }

        #endregion

        #region Variables
        private Facebook.FacebookOAuthResult _oAuthResult;
        private Facebook.FacebookClient _client;
        private FacebookDatabaseDataContext fbDataContext = new FacebookDatabaseDataContext();
        #endregion

        #region Public methods
        public Client(bool reuseAccessToken = true)
        {
            if (reuseAccessToken)
            {
                string accessToken = LoadSetting(Section, AccessToken, SettingFile);
                if (!string.IsNullOrWhiteSpace(accessToken))
                {
                    _client = new Facebook.FacebookClient(accessToken);
                }
            }

            _client.AppId = AppId;
            _client.AppSecret = AppSecret;

            try
            {
                _me = GetMe();
            }
            catch
            {
                Login();
                _me = GetMe();
            }

            fbDataContext.AddUser(Me);
        }

        private User GetMe()
        {
            dynamic result = _client.Get("fql", new
            {
                q = "SELECT uid, username, name, first_name, last_name, sex, " +
                        "profile_url FROM user WHERE uid = me()"
            });
            User user = Converter.FacebookUserToDatabaseUser(result.data[0]);
            return user;
        }

        public void FetchFriends()
        {
            dynamic result = _client.Get("fql", new
            {
                q = new
                {
                    friends = "SELECT uid2 FROM friend WHERE uid1 = me()",
                    users = "SELECT uid, username, name, first_name, last_name, sex, " +
                        "profile_url FROM user WHERE uid IN (SELECT uid2 FROM #friends)"
                }
            });

            foreach (dynamic query in result.data)
            {
                if (query.name == "users")
                {
                    dynamic users = query.fql_result_set;
                    foreach (dynamic row in users)
                    {
                        User user = Converter.FacebookUserToDatabaseUser(row);
                        fbDataContext.AddUser(user);
                    }
                }
            }
        }

        public void FetchComments()
        {
            double since = DateTimeConvertor.ToUnixTime(DateTime.UtcNow);

            bool empty = false;
            while (!empty)
            {
                var parameters = new
                {
                    q = new
                    {
                        friends = "SELECT uid2 FROM friend WHERE uid1 = me()",
                        posts = "SELECT message, actor_id, created_time, post_id, target_id " +
                            "FROM stream WHERE (source_id = me() OR source_id IN (SELECT uid2 FROM #friends)) " +
                            " AND message <> '' AND created_time < " + ((int)since).ToString() +
                            " ORDER BY created_time DESC LIMIT 5000",
                        comments = "SELECT id, text, fromid, time, post_id FROM comment WHERE post_id IN " +
                            "(SELECT post_id FROM #posts) AND fromid IN (SELECT uid2 FROM #friends)"
                    }
                };
                dynamic result = _client.Get("fql", parameters);
                foreach (dynamic query in result.data)
                {
                    string queryName = query.name as string;
                    switch (queryName)
                    {
                        case "friends":
                            break;
                        case "posts":
                            empty = FetchPosts(query.fql_result_set, ref since) == 0;
                            break;
                        case "comments":
                            FetchComments(query.fql_result_set);
                            break;
                    }
                }
            }
        }
        #endregion

        #region Private methods
        private void FetchComments(dynamic data)
        {
            foreach (dynamic row in data)
            {
                Comment c = new Comment();
                c.Id = row.id;
                c.OwnerUserId = row.fromid;
                c.Content = row.text;
                c.Time = row.time.ToString();
                c.FromUserId = c.OwnerUserId;
                c.ToUserId = c.OwnerUserId;
                fbDataContext.AddComment(c);
            }
        }

        private int FetchPosts(dynamic data, ref double since)
        {
            int count = 0;
            foreach (dynamic row in data)
            {
                Comment c = new Comment();
                c.Id = row.post_id;
                c.OwnerUserId = row.actor_id;
                c.Content = row.message;
                c.Time = row.created_time.ToString();
                c.FromUserId = c.OwnerUserId;
                c.ToUserId = (row.target_id == null ? c.OwnerUserId : row.target_id);
                fbDataContext.AddComment(c);
                ++count;
                since = double.Parse(c.Time);
            }
            return count;
        }

        private bool Login()
        {
            var dlg = new Dialog.Login(AppId, ExtendedPermissions);
            dlg.ShowDialog();

            if (dlg.FacebookOAuthResult != null)
            {
                _oAuthResult = dlg.FacebookOAuthResult;
                _client = new Facebook.FacebookClient(_oAuthResult.AccessToken);
                SaveSetting(Section, AccessToken, _oAuthResult.AccessToken, SettingFile);
                dlg.Dispose();

                return (_client != null);
            }
            else
            {
                return false;
            }
        }

        private string LoadSetting(string section, string key, string fileName)
        {
            FileIniDataParser fileIniData = new FileIniDataParser();
            string result;
            try
            {
                IniData parsedData = fileIniData.LoadFile(fileName);
                result = parsedData.Sections[section][key];
            }
            catch
            {
                result = null;
            }
            return result;
        }

        private void SaveSetting(string section, string key, string value, string fileName)
        {
            FileIniDataParser fileIniData = new FileIniDataParser();
            IniData parsedData;

            try
            {
                parsedData = fileIniData.LoadFile(fileName);
                parsedData.Sections[section][key] = value;
            }
            catch
            {
                parsedData = new IniData();
                parsedData.Sections.AddSection(section);
                parsedData[section].AddKey(key, value);
            }

            fileIniData.SaveFile(fileName, parsedData);
        }
        #endregion
    }
}
